﻿#ifndef YUTOPP_CLIENT_DETAIL_ASYNC_PART_HPP
#define YUTOPP_CLIENT_DETAIL_ASYNC_PART_HPP

#include "basic_api.hpp"
#include "../response.hpp"
#include "../error.hpp"

#include <string>
#include <utility>

#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <boost/type_traits/remove_cv.hpp>
#include <boost/range/adaptor/transformed.hpp>
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/repetition/repeat.hpp>
#include <boost/preprocessor/repetition/enum_params.hpp>
#include <boost/preprocessor/repetition/enum_trailing_params.hpp>

//
#define REQUEST_FUNCTION_TEMPLATE(n) \
	template<typename T BOOST_PP_ENUM_TRAILING_PARAMS(n, typename P), typename Handler>

#define REQUEST_FUNCTION_ARG( z, n, unused ) \
	BOOST_PP_CAT(BOOST_PP_CAT(const P, n),BOOST_PP_CAT(& p, n)) ,

#define REQUEST_FUNCTION( n, pre, type, sig ) \
	REQUEST_FUNCTION_TEMPLATE(n) \
	pre ## REQUEST_FUNCTION_ ## type ## _ARG_LIST(n,sig) \
	pre ## REQUEST_FUNCTION_ ## type ## _IMPL(n,sig)


//requestを定義
#define ASYNC_REQUEST_FUNCTION_COMMON_ARG_LIST( n, unused ) \
	void inline request( const binary_type& ext, BOOST_PP_REPEAT(n, REQUEST_FUNCTION_ARG, _) Handler& handler )

#define ASYNC_REQUEST_FUNCTION_COMMON_IMPL( n, unused ) \
	{ \
		typedef typename boost::remove_cv<T>::type helper_type; \
		typedef string_type* tag; \
		 \
		typedef boost::function< \
			void (const string_type&, const error::value&) \
		> formatted_handler_type; \
		 \
		static_cast<Derived&>(*this).template request_cushion<helper_type>( \
			ext, \
			helper_type()(tag() BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n,p) ), \
			boost::bind( \
				&decode_frame<formatted_handler_type, translator::decoder_type, string_type>, \
				formatted_handler_type( boost::bind( handler, _1, _2 ) ), \
				_1, _2 \
				) \
			); \
	}

#define ASYNC_REQUEST_FUNCTION_COMMON( z, n, unused ) \
	REQUEST_FUNCTION( n, ASYNC_, COMMON, _ )

#define DEFINE_ASYNC_REQUEST_FUNCTION_COMMON( n ) \
	BOOST_PP_REPEAT( \
		BOOST_PP_ADD( 1, n ), \
		ASYNC_REQUEST_FUNCTION_COMMON, \
		_ \
	)


//request_[hogehoge]を定義
#define ASYNC_REQUEST_FUNCTION_CUSTOM_ARG_LIST(n,sig) \
	void request_ ## sig ## ( BOOST_PP_REPEAT(n, REQUEST_FUNCTION_ARG, _) Handler& handler )

#define ASYNC_REQUEST_FUNCTION_CUSTOM_IMPL(n,sig) \
	{ \
		typedef typename boost::remove_cv<T>::type	helper_type; \
		typedef string_type* tag; \
		 \
		typedef boost::function< \
			void (const container::basic_response<string_type>&, const error::value&) \
		> formatted_handler_type; \
		 \
		static_cast<Derived&>(*this).template request_cushion<helper_type>( \
			BOOST_PP_STRINGIZE( sig ), \
			helper_type()(tag() BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n,p) ), \
			boost::bind( \
				&make_ ## sig ## _translated_caller<formatted_handler_type, translator::decoder_type, string_type>, \
				formatted_handler_type( boost::bind( handler, _1, _2 ) ), \
				_1, _2 \
				) \
			); \
	}

#define ASYNC_REQUEST_FUNCTION_CUSTOM( z, n, sig ) \
	REQUEST_FUNCTION( n, ASYNC_, CUSTOM, sig )

#define DEFINE_ASYNC_REQUEST_FUNCTION_CUSTOM( sig, n ) \
	BOOST_PP_REPEAT( \
		BOOST_PP_ADD( 1, n ), \
		ASYNC_REQUEST_FUNCTION_CUSTOM, \
		sig \
	)


namespace webapp
{
	namespace clients
	{
		namespace detail
		{
			template<typename Handler, typename Decoder, typename String>
			void decode_frame( Handler handler, const String& body, const error::value& e )
			{
				Decoder d;
				handler( d( url::decode( body ) ), e );
			}

			template<typename Handler, typename Decoder, typename String>
			void make_xml_translated_caller( Handler handler, const String& data, const error::value& error )
			{
				Decoder d;
				handler( xml_tree_helper( d( url::decode( data ) ) ), error );
			}

			template<typename Handler, typename Decoder, typename String>
			void make_json_translated_caller( Handler handler, const String& data, const error::value& error )
			{
				Decoder d;
				handler( json_tree_helper( d( url::decode( data ) ) ), error );
			}

			//コールバックが必須
			template<typename Derived, typename Policy>
			class async_part
			{
				typedef typename Policy::string_type		string_type;
				typedef std::string							binary_type;
				typedef typename Policy::translator_type	translator;



			public:
				// リクエストの送信(結果は素の文字列)
				DEFINE_ASYNC_REQUEST_FUNCTION_COMMON( 7 )
				// リクエストの送信(結果はrequest型)
				DEFINE_ASYNC_REQUEST_FUNCTION_CUSTOM( xml, 7 )
				DEFINE_ASYNC_REQUEST_FUNCTION_CUSTOM( json, 7 )

				//
				template<typename Handler>
				void raw_urlencoded_request(
					const binary_type& method,
					const binary_type& path,
					const binary_type& ext,
					const binary_type& body,
					Handler& handler )
				{
					typedef request::content::url_encoded		ue;
					typedef ue::template traits<string_type>	traits;
					typedef typename traits::parameter_type		parameter_type;

					if ( !body.empty() ) {
						//内容を分解する
						const parameter_type parameter = util::parse_url_encoded_text( body );

						if ( !parameter.empty() ) {
							raw_urlencoded_request( method, path, ext, parameter, handler );

						} else {
							boost::bind( handler, _1, _2 )( "", error::value( error::critical, "invalid parameter." ) );
						}

					} else {
						raw_urlencoded_request( method, path, ext, parameter_type(), handler );
					}
				}
			
				template<typename Handler>
				void raw_urlencoded_request(
					const binary_type& method,
					const binary_type& path,
					const binary_type& ext,
					const typename request::content::url_encoded::template traits<string_type>::parameter_type& parameter,
					Handler& handler )
				{
					typedef request::content::url_encoded ue;
					typedef boost::function<
						void (const string_type&, const error::value&)
					> formatted_handler_type;

					static_cast<Derived&>(*this).template request_cushion<ue>(
						static_cast<Derived&>(*this).get_address( method, path, ext ),
						parameter,
						static_cast<Derived&>(*this).generate_body( parameter, ue() ),
						ue(),
						boost::bind(
							&decode_frame<formatted_handler_type, translator::decoder_type, string_type>,
							formatted_handler_type( boost::bind( handler, _1, _2 ) ),
							_1, _2
							)
						);
				}

/*				template<class Handler>
				void raw_form_data_request(
					  const string_type& method
					, const string_type& name
					, const string_type& ext
					, const string_type& body
					, const string_type& boundary
					, Handler& handler )
				{
					const auto address = static_cast<Derived&>(*this).get_address( name, ext );		
					static_cast<Derived&>(*this).send_request(
						method,
						std::get<1>( address ),
						static_cast<Derived&>(*this).make_auth_header( method, std::get<2>( address ), param_type() ),
						body,
						boost::bind( handler, _1, _2 ),
						boundary
						);
				}*/
			};
		} // - detail
	} // - clients
} // - webapp


#undef REQUEST_FUNCTION_TEMPLATE
#undef REQUEST_FUNCTION_ARG
#undef REQUEST_FUNCTION

#undef ASYNC_REQUEST_FUNCTION_COMMON_ARG_LIST
#undef ASYNC_REQUEST_FUNCTION_COMMON_IMPL
#undef ASYNC_REQUEST_FUNCTION_COMMON
#undef DEFINE_ASYNC_REQUEST_FUNCTION_COMMON

#undef ASYNC_REQUEST_FUNCTION_CUSTOM_ARG_LIST
#undef ASYNC_REQUEST_FUNCTION_CUSTOM_IMPL
#undef ASYNC_REQUEST_FUNCTION_CUSTOM
#undef DEFINE_ASYNC_REQUEST_FUNCTION_CUSTOM

#endif /*YUTOPP_CLIENT_DETAIL_ASYNC_PART_HPP*/
